<?php
/*
	Copyright 2006-2007 Alex Djachenko http://www.my-site.ru
	Authors: Alex Djachenko
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

class sharpobj_storage_mysql extends sharpobj_storage_proto_sql
{
	private $connect=null;
	private $connect_read=null;
	private $format=null;

    /**
     * Сделать sql-запрос к базе данных (функция низкого уровня)
     * $type - тип ожидаемого результата запроса
     *    sqlres - не обработанный результат
     *    assocvalue - одна строка, переведенная в ассоциативный массив
     *    rowvalue - одна строка, переведенная в нумерованный массив
     *    onevalue - первая запись первой строки результата
     *    action - возвращает true, если запрос выполнен без ошибок
     *    insert - возвращает id последней вставленной записи
     * $sql - sql-запрос
     *
     * @param string $sql
     * @param string $type
     */
    function sqlquery($sql,$type,$readonly=false)
	{
		$res = null;
		$sql = $this->queryReplace($sql);
		$connect = &$this->connect;
		if ($readonly AND
		        $this->connect_read)
		{
		    // Соединение  с сервером только для чтения отсутствует
            $connect = &$this->connect_read;
		}
		
		if (!($sqlres = mysql_query($sql,$connect)))
		{
			$type = 'error';
			mst_error('Ошибка в sql-запросе',"Ошибка (".mysql_error().") в запросе ({$sql})",2);
		}
		switch ($type)
		{
			case 'sqlres':
				$res = $sqlres;
			break;
			case 'assocvalue':
				$res = mysql_fetch_assoc($sqlres);
				mysql_free_result($sqlres);
			break;
			case 'rowvalue':
				$res = mysql_fetch_row($sqlres);
				mysql_free_result($sqlres);
			break;
			case 'onevalue':
				if (mysql_num_rows($sqlres)<1)
				{
					// Не возвращено ни одной строки
					$res = false;
				}else
				{
					//
					$res = mysql_result($sqlres,0,0);
				}
				mysql_free_result($sqlres);
			break;
			case 'action':
				// mysql_free_result($sqlres);
				$res = true;
			break;
			case 'insert':
				// $res = mysql_insert_id();
				$res = $this->id2obj(mysql_insert_id());
				// mysql_free_result($sqlres);
			break;
			case 'error':
				// mysql_free_result($sqlres);
				$res = false;
			break;
			default:
				mst_error('Неправильный тип запроса','Тип запроса '.$type.' не поддерживается',2);
				$res = false;
			break;
		}
		return $res;
	}

	// *******************************
	// Базовый набор функций работы с записями
	// *******************************
    /**
     * Получить запись по ее id
     *
     * @param int $id
     * @return bool
     */
    function get($id)
	{
	    $idobj = $this->id2obj($id);
	    return $this->sqlqueryGet(" WHERE `{$this->idfield}`='{$idobj->get('sql')}'");
	}
    /**
     * Вставить запись в базу данных
     *
     * @param sharpobj_data_interface_item $data
     */
    function insert(sharpobj_data_interface_item $data)
	{
		// Формируем SQL-запрос
		$sql = 'INSERT INTO `'.$this->tbname.'`'
		.' ('
		.$this->format->getColNames(null, '`', ', ','`'.$this->tbname.'`.')
		.')'
		.' VALUES ('
		.$this->format->getColVals($data,'sql',"'", ', ')
		.');';
		// echo $sql;
		return $this->sqlquery($sql,'insert');
	}
    /**
     * Обновить запись с $id
     * Если $emptyreplace == true - пустые записи тоже обновляются
     * Если $id == null - id береться из контейнера
     *
     * @param int $id
     * @param sharpobj_data_interface_item $data
     * @param bool $emptyreplace
     * @param sharpobj_data_interface_item $data
     */
    function update(sharpobj_data_interface_item $data,$id=null,$emptyreplace=false)
	{
		// Создаем контейнер для id
		$idobj = $this->id2obj($id);
		
		if ($idobj->get('plain')>0)
		{
			// ID задан принудительно
			// $idobj->set('plain',$id);
		}elseif ($data instanceof sharpobj_data_item_data  AND $data->getId()>0)
		{
			// ID задан в объекте
			$idobj = new sharpobj_data_item_int();
			$idobj->set('plain',$data->getId());
		}else
		{
			mst_error('Не передан id',"Для операции UPDATE необходимо задать id записи "
			."принудительно или передать вместе с контейнером",1);
			return false;
		}
		// Формируем SQL-запрос
		$sql = 'UPDATE `'.$this->tbname.'`'
		.' SET '
		.$this->format->getColNameVals($data,'sql',
					"`", '`'.$this->tbname.'`.',"'",'=',', ',
					!$emptyreplace)
		.' WHERE `'.$this->idfield.'`='
		."'".$idobj->get('sql')."'"
		.' LIMIT 1';

		return $this->sqlquery($sql,'action');
	}
    /**
     * Удалить запись по ее id
     *
     * @param int $id
     * @return bool
     */
    function delete($id)
	{

		$idobj = $this->id2obj($id);
		// Формируем SQL-запрос
		$sql = 'DELETE FROM `'.$this->tbname.'`'
		.' WHERE `'.$this->idfield.'`='
		."'".$idobj->get('sql')."'"
		.' LIMIT 1';
		// echo $sql;
		return $this->sqlquery($sql,'action');
	}
	
	function count()
	{
		return intval($this->sqlquery("SELECT count(*) FROM #!table#",'onevalue'));
	}
	
	/**
	 *  Получить список всех записей в БД	
	 * @return void
	 */
	function getListAll($from=null,$limit=null)
	{
		//
		return $this->sqlqueryList("SELECT #!id# FROM #!table# "
			.$this->sqlgenLimit($from,$limit));
	}
	// *******************************
	// Дополнительные функции работы с записями
	// *******************************
	function countUp($id,$field)
	{
		//
		$field = addslashes($field);
		// Создаем контейнер для id
		$idobj = $this->id2obj($id);
		if ($idobj->get('plain')<=0)
		{
			mst_error('Не передан id',
					"Для операции countUp необходимо задать id записи ",1);
			return false;
		}
		return $this->sqlquery("UPDATE #!table# "
						." SET `{$field}`=(`{$field}`+1) "
						."WHERE #!id#='{$idobj->get('sql')}'",'action');		
	}
	function countDown($id,$field)
	{
		//
		$field = addslashes($field);
		// Создаем контейнер для id
		$idobj = $this->id2obj($id);
		if ($idobj->get('plain')<=0)
		{
			mst_error('Не передан id',
					"Для операции countUp необходимо задать id записи ",1);
			return false;
		}
		return $this->sqlquery("UPDATE #!table# "
						." SET `{$field}`=(`{$field}`-1) "
						."WHERE #!id#='{$idobj->get('sql')}'",'action');	
	}
	/**
	 *  Получить список всех записей в БД	
	 * @return void
	 */
	function getListFilter($filter,$from=null,$limit=null)
	{
		//
        $filter_sql = '';
        // Создаем фильтр
        foreach ($filter as $key=>$val)
        {
            // Получаем клон элемента нужного формата
            $item = $this->format->getObj($key);
            $item->clear();
            // Устанавливаем данные
            $item->set('plain',$val);
            $field = addslashes($this->format->getColName($key));
            // Добавляем условие
            if (mb_strlen($filter_sql)>0){$filter_sql .= ' AND';}
            $filter_sql .= " `{$field}`={$item->get('sql')} ";
            
        }
	    if (mb_strlen($filter_sql)>0){$filter_sql = ' WHERE '.$filter_sql;}
		return $this->sqlqueryList("SELECT #!id# FROM #!table# ".$filter_sql
			.$this->sqlgenLimit($from,$limit));
	}
	// *******************************
	// Дополнительные функции, для работы с sql-БД
	// *******************************
    /**
     * Выбрать один объект из базы по sql-условию
     * @param string $sql
     */	
    function sqlqueryGet($sqlwhere)
	{
		// $idobj = new sharpobj_data_item_int();
		// $idobj->set('plain',$id);
		// $idobj = $this->id2obj($id);
		// Формируем SQL-запрос
		$sql = 'SELECT '
		."`{$this->idfield}`,".$this->format->getColNames(null,  '`', ', ','`'.$this->tbname.'`.')
		.' FROM `'.$this->tbname.'`'
		.' '.$sqlwhere
		// .' WHERE `'.$this->idfield.'`='
		// ."'".$idobj->get('sql')."'"
		.' LIMIT 1';
		// echo $sql;
		$mysqlres = $this->sqlquery($sql,'rowvalue',true);
		// Проверяем наличие результата
		if (false===$mysqlres)
		{	// Результат отсутствует
			return false;
		}
		// Получаем список колонок и полей
		$fields = $this->format->getColsArray(null, false);
		// Клонируем пустой объект с данными
		$dataobj = $this->format->getObj();
        // Устанавливаем id записи
        $thisres = each($mysqlres);
		$dataobj->setId($thisres['value']);
		// Заполняем объект данными
		foreach ($fields as $field_datapath=>$field_colname)
		{
			// Получаем соответствующую колонку из результата запроса
			$thisres = each($mysqlres);
			// Помещаем данные в объект
			$dataobj->set('sql',$thisres['value'],$field_datapath);
			
		}
		return $dataobj;
	}
    /**
     * Выбрать список объектов из базы по sql-запросу
     * В первой колонке должны идти ID записей в данном хранилище
     * Остальные поля не важны
     * @param string $sql
     */	
    function sqlqueryList($sql)
	{
		// Создаем контейнер для результата
		$result = new sharpobj_data_item_array();
		// Выполняем запрос к БД
		if (!($sqlres = $this->sqlquery($sql,'sqlres',true)))
		{
			return $result;
		}
		while ($res = mysql_fetch_row($sqlres))
		{
			//
			$result->add(null,$this->get($res[0]));
		}
		// Очищаем результат запроса
		mysql_free_result($sqlres);
		return $result;
	}
    /**
     * Выбрать одно значение из БД
     * @param string $sql
     */	
    function sqlqueryOneValue($sql)
	{
		return $this->sqlquery($sql,'onevalue',true);
	}
    /**
     * Выбрать одно integer-значение из БД
     * @param string $sql
     */
    function sqlqueryOneValueInt($sql)
	{
		$res = $this->sqlquery($sql,'onevalue',true);
		if (!sharpobj_iserror($res) AND $res !==false)
		{
		    $res = intval($res);
		}
		return $res;
	}
    /**
     * Выбрать нумерованный массив из базы
     * @param string $sql
     */
    function sqlqueryRowValue($sql)
	{
		return $this->sqlquery($sql,'rowvalue',true);
	}
    /**
     * Выбрать ассоциативный массив из базы
     * @param string $sql
     */	
    function sqlqueryAssocValue($sql)
	{
		return $this->sqlquery($sql,'assocvalue',true);
	}
    /**
     * Выполнить действие в базе данных
     * @param string $sql
     */	
    function sqlqueryAction($sql)
	{
		return $this->sqlquery($sql,'action');
	}
	// *******************************
	// Вспомогательные функции
	// *******************************
	function getObj($datapath=null)
	{
		return $this->format->getObj($datapath);
	}
    function setConnection(&$connect,$readonly=false)
	{
		if ($readonly)
	    {
		    $this->connect_read = &$connect;
	    }else
	    {
	        $this->connect = &$connect;
	    }
		
	}
    function &getConnection($readonly=false)
	{
	    if ($readonly)
	    {
		    return $this->connect_read;
	    }else
	    {
	        return $this->connect;
	    }
	}

	// *******************************
	// Служебные функции для внутреннего пользования
	// *******************************
	function __construct(sharpobj_storage_format $format, $opt)
	{
		$this->format = $format;
		$this->tbname = $opt['tbname'];
		$this->idfield = $opt['idfield'];
		// Добавляем соединение по умолчанию (потом можно переопределить)
		$this->setConnection(sharpobj_dbconn_get('mysql'));
		$this->setConnection(sharpobj_dbconn_get('mysql','readonly',true),true);
	}	
    

}
// Объявляем класс под старым именем для совместимости
class sharpobj_storage_simplemysql extends sharpobj_storage_mysql
{

}
?>